package com.botann.projects.service;

import com.botann.projects.base.BaseService;
import com.botann.projects.base.UserDetail;
import com.botann.projects.model.AdminUser;
import com.botann.projects.model.Menu;
import com.botann.projects.model.RoleMenu;
import com.botann.projects.base.BaseService;
import com.botann.projects.base.UserDetail;
import com.botann.projects.base.UserDetailType;
import com.botann.projects.model.AdminUser;
import com.botann.projects.model.Menu;
import com.botann.projects.model.RoleMenu;
import com.sun.istack.internal.NotNull;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

/**
 * Charging
 * MenuService
 * Created by 刘山国 on 2017/11/23.
 * Copyright (c) 2017 刘山国. All rights reserved
 */
@Service
public class MenuService extends BaseService implements MenuServiceIntf {

    @Override
    public Class loggerClass() {
        return MenuService.class;
    }


    @Override
    public Menu loadMenuWithName(String menuName) {
        return sessionGet(Menu.class,"menuName=",menuName.trim());
    }


    @Override
    public Menu loadMenuByByPath(String path) {
        return sessionGetNN(Menu.class,"url=",path);
    }

    @Override
    public List<Menu> loadMenus() {
        List<Menu> menus = sessionQuery(Menu.class,"menuIndex",conditions("parentId="),0);
        getChildrenMenus(menus,null,null);
        return menus;
    }

    @Override
    public List<Menu> loadMenusOfRole(Integer roleId) {
        UserDetail userDetail = UserDetail.current();
        if (userDetail.getType() != UserDetailType.USER_ADMIN) throw sgErrorRuntimeException("暂无权限");
        AdminUser adminUser = sessionGet(AdminUser.class,userDetail.getId());
//        if (userDetail.getType() != UserDetailType.USER_ADMIN) throw sgErrorRuntimeException("暂无权限");
//        AdminUser adminUser = sessionGet(AdminUser.class,1);
        if (roleId == null) roleId = adminUser.getRoleId();
        StringBuilder hqlBuilder = selectNewBuilder(Menu.class)
                .append(modelFields(Menu.class,"m"))
                .append(") from RoleMenu rm inner join Menu m on rm.menuId = m.id ")
                .append("where rm.roleId =?0 and m.parentId=?1 order by m.menuIndex");
        List<Menu> menus = sessionQuery(hqlBuilder.toString(),roleId,0);
        getChildrenMenus(menus,hqlBuilder.toString(),roleId);
        return menus;
    }

    @Override
    public List<Integer> loadMenuIdsOfRole(@NotNull Integer roleId) {
        StringBuilder hqlBuilder = selectNewBuilder(RoleMenu.class)
                .append(modelFields(RoleMenu.class,"rm"))
                .append(") from RoleMenu rm inner join Menu m on rm.menuId = m.id and m.parent=false ")
                .append("where rm.roleId =?0 order by m.menuIndex");
        List<RoleMenu> roleMenus = sessionQuery(hqlBuilder.toString(),roleId);
        List<Integer> ids = new ArrayList<>();
        roleMenus.forEach((RoleMenu roleMenu) -> ids.add(roleMenu.getMenuId()));
        return ids;
    }

    // 生成树
    private void getChildrenMenus(List<Menu> menuModels, String hql, Integer typeOrRoleId) {
        for (int i=0;i<menuModels.size();i++) {
            Menu menu = menuModels.get(i);
            List<Menu> childrenMenuModels;
            if (hql == null) {
                childrenMenuModels = sessionQuery(Menu.class,"menuIndex",conditions("parentId="),menu.getId());
            } else {
                childrenMenuModels = sessionQuery(hql,typeOrRoleId,menu.getId());
            }
            menu.setChildren(childrenMenuModels);
            getChildrenMenus(childrenMenuModels,hql,typeOrRoleId);
        }
    }







    /**
     * 递归上寻补足parentMenuId 到 allNewMenuIds
     * @param allNewMenuIds List<Integer>
     * @param newMenuId 当前menuId
     */
    private void resoleAllNewIds (List<Integer> allNewMenuIds,Integer newMenuId) {
        if (newMenuId > 0) {
            Menu menu = sessionGet(Menu.class,newMenuId);
            if (!allNewMenuIds.contains(menu.getParentId()) && menu.getParentId() > 0) {
                allNewMenuIds.add(menu.getParentId());
            }
            resoleAllNewIds(allNewMenuIds,menu.getParentId());
        }

    }

    @Override
    public void updateRoleMenuIds(String idsJson,Integer roleId) {
        List<Integer> ids = jsonToListModel(idsJson,Integer.class);
        updateRoleMenuIds(ids,roleId);
    }
    private void updateRoleMenuIds(List<Integer> newMenuIds,Integer roleId) {
        List<Integer> allNewMenuIds = new ArrayList<>(newMenuIds);
        newMenuIds.forEach((Integer newMenuId) -> resoleAllNewIds(allNewMenuIds,newMenuId) );
        List<RoleMenu> originRoleMenus = sessionQuery(RoleMenu.class,"roleId=",roleId);
        List<Integer> originIds = new ArrayList<>();

        // 删除
        originRoleMenus.forEach((RoleMenu originMenu) -> {
            originIds.add(originMenu.getMenuId());
            if (!allNewMenuIds.contains(originMenu.getMenuId())) {
                sessionForceDelete(originMenu);
            }
        });

        // 新增
        allNewMenuIds.forEach((Integer newMenuId) -> {
           if (!originIds.contains(newMenuId)) {
               RoleMenu roleMenu = new RoleMenu();
               roleMenu.setRoleId(roleId);
               roleMenu.setMenuId(newMenuId);
               sessionFlushClear();
               sessionSave(roleMenu);
           }
        });
    }


    @Override
    public void updateMenus(String menusJson, String deleteIdsJson) {

        // 新增或者更新一些menu
        List<Menu> menus = jsonToListModel(menusJson,Menu.class);
            //把集合中的每个元素，都与数据库中根据相同Menu id取出来的 oldMenu 比较，如果不同就sessionUpdate/如果相同就sessionSave
        for (int i=0;i<menus.size();i++) {
            Menu menu = menus.get(i);
            menu.setMenuIndex(i); // menuIndex 同级Menu保证升序就可以？-   是可以重复的吗？-
                                 //---不会重复的，因为下面的方法中会全部对每个Menu 进行Update/save，不管哪种，它的menuIndex都是这里设置的升序
                                // menu_index` int(11) NOT NULL DEFAULT '0' COMMENT '菜单顺序,所有用户使用同一个顺序',
            updateOrSaveMenu(menu,null);
        }

        // 删除一些menu,递归删除
        List<Integer> deleteIds = jsonToListModel(deleteIdsJson,Integer.class);
        deleteIds.forEach((Integer menuId) -> {
            if (menuId > 0){    //表示不是新增的
                Menu menu = sessionGet(Menu.class,menuId);
                deleteMenu(menu);
            }
        });
    }

    /**
     * 递归更新或新增Menu
     * @param menu          Menu
     * @param parentMenu    Menu
     */
    private void updateOrSaveMenu(Menu menu, Menu parentMenu) {
        if (menu.getId() > 0){// ---表示是更新
            Menu oldMenu = sessionLoad(Menu.class,menu.getId());//这里为什么要用懒加载？ ---减少了内存的开销
            if (!oldMenu.equals(menu)) {//实体类中重写了 equals方法：比较两个menu的内容是否相同
                sessionFlushClear();//session的缓存中的数据与数据库同步。也就是持久化到数据库中的
                sessionUpdate(menu);
            }
        } else {                //---表示新增的MENU
            if (parentMenu != null) menu.setParentId(parentMenu.getId());
            sessionSave(menu);
        }
        for (int i=0;i<menu.getChildren().size();i++) {         //这步是做什么用的？？？ 为什么.getChildren 就能得到子菜单？
            Menu childMenu = menu.getChildren().get(i);         //可能是前端传过来的menu中就封装了children
            childMenu.setMenuIndex(i);
            sessionFlushClear();
            updateOrSaveMenu(childMenu,menu);
        }
    }

    /**
     * 递归删除 Menu 1
     * @param menu Menu
     */
    private void deleteMenu(Menu menu) {
        if (menu != null) {
            sessionForceDelete(menu);
            List<RoleMenu> roleMenus = sessionQuery(RoleMenu.class,"menuId=",menu.getId());
            roleMenus.forEach((RoleMenu roleMenu) -> sessionForceDelete(roleMenu) );

            List<Menu> childrenMenus = sessionQuery(Menu.class,"parentId=",menu.getId());
            childrenMenus.forEach((Menu childMenu) -> deleteMenu(childMenu) );
        }
    }


}
